﻿/*
This file is part of quIRC bot

    quIRC bot is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    quIRC bot is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with quIRC bot.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "irc.h"

void MessageHandler(void *nul);

COMMANDLIST COMMAND[] =
{
	{"help",	"Display commands",			&cmd_help},
	{"about",	"About",					&cmd_about},
	{"slap",	"Test command",				&cmd_slap},
	{"join",	"Join #channel",			&cmd_join},
	{"setname", "Set the bot's name",		&cmd_setname},
	{"part",	"Disconnect from channel",	&cmd_part},
	{"quit",	"Disconnect and shut down", &cmd_quit}
};

char sent_from[256];
char params[5][256];		// 5 different params supported, 256 chars each
char channel_from[256];

char irc_buffer[IRC_LINE];

IRC_CONNECTION::IRC_CONNECTION()
{
	// constructor
	DomainHost = 0;
	ZeroMemory(&DomainAddr, sizeof(in_addr));
	IsConnected = false;
}

IRC_CONNECTION::~IRC_CONNECTION()
{
	// deconstructor
}


bool IRC_CONNECTION::IRCInit()
{
	int retval = WSAStartup(MAKEWORD(2,2), &wsa);
	// check for an invalid
	if(retval != 0)
		return false;
	// check for an out of date dll
	if(LOBYTE(wsa.wVersion) != 2 || HIBYTE(wsa.wVersion) != 2 ) 
	{
		WSACleanup();
		return false;
	}
	else return true;
}

bool IRC_CONNECTION::IRCClose()
{
	return WSACleanup();
}

bool IRC_CONNECTION::ResolveDomain(const char* DomainName)
{
#ifdef DEBUG_MODE
	printf("\nAttempting to get host with name %s\n", DomainName);
#endif

	DomainHost = gethostbyname(DomainName);
	// OH FUCK ERROR CHECKING???
	if(DomainHost == NULL)
	{
		DWORD dwError = WSAGetLastError();
		if(dwError == WSAHOST_NOT_FOUND)
		{
		  #ifdef DEBUG_MODE
			printf("Host not found\n");
		  #endif
			return false;
		}
		else if(dwError = WSANO_DATA)
		{
		  #ifdef DEBUG_MODE
			printf("No data records were found\n");
		  #endif
			return false;
		}
		else 
		{
		  #ifdef DEBUG_MODE
			printf("Could not get host. Error %ld\n", dwError);
		  #endif
			return false;
		}
	}

	info.server = (char*)DomainName;

	DomainAddr.S_un.S_addr = *(u_long*)DomainHost->h_addr_list[0];
#ifdef DEBUG_MODE
	printf("IP address of %s : %s\n\n", DomainName, inet_ntoa(DomainAddr));
#endif
	return true;
}

int IRC_CONNECTION::irc_send(const char* format, ...)
{
	if(!IsConnected)
	{
	  #ifdef DEBUG_MODE
		printf("Must be connected before you can use send()");
	  #endif
		return -1;
	}

	va_list args;
	ZeroMemory(&irc_buffer, IRC_LINE);

	// put all our args into the buffer
	va_start(args, format);
	vsnprintf(irc_buffer, IRC_LINE, format, args);
	va_end(args);

	// add close
	strncat(irc_buffer, "\n", 2);

  #ifdef DEBUG_MODE
	printf(">> %s", irc_buffer);
  #endif
	return send(info.sock, irc_buffer, strlen(irc_buffer), 0);
}

char* IRC_CONNECTION::irc_recv()
{
	if(!IsConnected)
		return 0;

	char buf = 0;	// mini buffer
	WORD pos = 0;	// will count up as we recieve bytes

	ZeroMemory(&irc_buffer, IRC_LINE);

	// recieve byte by byte until we reach \n / \r char
	while (buf != '\n' && strlen(irc_buffer) < IRC_LINE)
	{
		if (!recv(info.sock, (char *) &buf, 1, 0))
			return 0;

		if (buf != '\n' && buf != '\r' && buf != '\0')
		{
			strncat(irc_buffer, (char *) &buf, 1);
		}
	}
  
  #ifdef SUPER_DEBUG_MODE
	printf("<< %s\n", irc_buffer);
  #endif
	return irc_buffer;
}

bool IRC_CONNECTION::JoinServer(const char* Server, int Port, const char* Channel, const char* Pass, const char* NickName, const char* User)
{
	if(!ResolveDomain(Server))
	{
	  #ifdef DEBUG_MODE
		printf("Could not connect to server.");
	  #endif
		return false;
	}

	// struct to hold sockinfo
	insock.sin_family = PF_INET;
	insock.sin_port = htons(Port);
	insock.sin_addr.s_addr = inet_addr(inet_ntoa(DomainAddr));
	

	info.sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(info.sock == -1)
	{
	  #ifdef DEBUG_MODE
		printf("Error creating socket!\n");
	  #endif
		return false;
	}
	else 
	{
  #ifdef DEBUG_MODE
		printf("- Socket created.\n");
  #endif 
	}

	DWORD dwError = connect(info.sock, (sockaddr *)&insock, sizeof(insock));
	if(dwError != 0)
	{
	  #ifdef DEBUG_MODE
		DWORD dwErrorHandler = WSAGetLastError();
		printf("Error connecting to %s:%i! Error %i\n", Server, Port, dwErrorHandler);
	  #endif
		return false;
	}
	else 
	{
  #ifdef DEBUG_MODE
		printf("- Connected to socket.\n");
		printf("- Now connected to %s:%i\n\n", Server, Port);
  #endif 
		IsConnected = true;
	}

	// Now let's set our nick
	irc_send("NICK %s", NickName);

	// Send info
	irc_send("USER %s 127.0.0.1 localhost :%s", NickName, User);

	// Now let's set the description to the computer name
	const char* ComputerName = getenv("COMPUTERNAME");
	irc_send("DESC %s", ComputerName);
	
	// Mode
	irc_send("MODE %s:+i", NickName);

	// Protoctl
	irc_send("PROTOCTL NAMESX");

	// Userhost
	irc_send("USERHOST %s", NickName);

	return true;
}

bool IRC_CONNECTION::JoinChannel(const char* Channel)
{
	if(!IsConnected)
	{
	  #ifdef DEBUG_MODE
		printf("Must be connected before you can join a channel!\n");
	  #endif
		return false;
	}

	DWORD dwError;

	ZeroMemory(&irc_buffer, IRC_LINE);
	sprintf(irc_buffer, "JOIN #%s\r\n", Channel);
	dwError = send(info.sock, irc_buffer, strlen(irc_buffer), IPPROTO_TCP);

	ZeroMemory(&irc_buffer, IRC_LINE);
	sprintf(irc_buffer, "MODE #%s\r\n", Channel);
	dwError = send(info.sock, irc_buffer, strlen(irc_buffer), IPPROTO_TCP);

	return true;
}


int IRC_CONNECTION::MessageLoop()
{
	bool bLoop = true;
	char* token;

	Sleep(2000);
	

	while(bLoop)
	{
		token = 0;
		ZeroMemory(&irc_buffer, IRC_LINE);
		
		irc_recv();

		// Now check if we have a PING request
		token = strtok(irc_buffer, " ");
		argsc = 0;

		while(token != NULL)
		{
			argsv[argsc] = token;
			token = strtok(NULL, " ");
			argsc++;
		}
	
	// Switches don't work without enum... too lazy :P
		// raw numerics http://www.mirc.net/raws/

		if(strcmp(argsv[1], "001") == 0)	// welcome
		{
			irc_send("JOIN %s :%s", IRC_Channel, IRC_Pass);
		}
		if(strcmp(argsv[1], "332") == 0)	// nickname conflict
		{
			const char* chan_topic = argsv[4];
		}
		if(strcmp(argsv[1], "510") == 0)	// nickname conflict
		{
			irc_send("NICK %s :%s", IRC_User);
			irc_send("JOIN %s :%s", IRC_Channel, IRC_Pass);
		}
		if(strcmp(argsv[0], "PING") == 0)	// ping requests
		{
			ZeroMemory(&params, 256 * 5);
			sprintf(params[0], "PONG %s", argsv[1]);
			irc_send("%s", params[0]);
		}
		if(argsv[3][1] == COMMAND_TRIGGER)	// a command
		{
			for(int i = 0; i < sizeof(COMMAND) / sizeof(COMMANDLIST); i++)	// scroll through our commandlist
			{
				if(strcmp(argsv[3] + 2, COMMAND[i].Command) == 0)
				{
					ZeroMemory(&params, 256 * 5);			// argv[0] === Sent from
					ZeroMemory(&sent_from, 256);			// argv[1] === Channel from
					ZeroMemory(&sent_from, 256);			// argv[3] === text 1 (command)
					strcpy(sent_from, argsv[0]);			// argv[4] === param 1
					strcpy(channel_from, argsv[2]);			// argv[5] === param 2... etc
					for(int x = 0; x < argsc; x++)
						strcpy(params[x], argsv[x + 4]);
					
					COMMAND[i].exec_command(this, &irc_buffer, &argsv[0]);
					break;
				}
			}
		}
		

		Sleep(200);
	}
	return 0;
}

// BASIC COMMANDS
// do not edit/remove!
void cmd_help(IRC_CONNECTION* m_irc, LPVOID parameters, LPVOID from)
{	
	for(int i = 0; i < sizeof(COMMAND) / sizeof(COMMANDLIST); i++)
	{
		m_irc->irc_send("PRIVMSG %s :%s%s", channel_from, COMMAND[i].Command, COMMAND[i].Usage);
	}
	return;
}
void cmd_about(IRC_CONNECTION* m_irc, LPVOID parameters, LPVOID from)
{	
	m_irc->irc_send("PRIVMSG %s :quIRC - a customizable irc bot by Tox1k and hELWiRE!", channel_from);
	return;
}
void cmd_join(IRC_CONNECTION* m_irc, LPVOID parameters, LPVOID from)
{
	m_irc->irc_send("JOIN %s :%s", params[0], "");
}
void cmd_setname(IRC_CONNECTION* m_irc, LPVOID parameters, LPVOID from)
{
	m_irc->irc_send("NICK %s", params[0], "");
}
void cmd_part(IRC_CONNECTION* m_irc, LPVOID parameters, LPVOID from)
{
	m_irc->irc_send("PART %s :%s", params[0], "");
} 
void cmd_quit(IRC_CONNECTION* m_irc, LPVOID parameters, LPVOID from)
{
	m_irc->irc_send("QUIT :%s", params[0]);
	m_irc->IRCClose();
	WSACleanup();
	exit(0);
}

// OTHER COMMANDS
// add stuff here
const char* slapcommands[] =
{
	" with a dead fish",
	" with a prostitute's cunt rag",
	" with a frying pan",
	" "
};

void cmd_slap(IRC_CONNECTION* m_irc, LPVOID parameters, LPVOID from)
{	
	unsigned char mandate = rand() % (sizeof(slapcommands) / sizeof(const char*));
	m_irc->irc_send("PRIVMSG %s :%sACTION slapped %s%s%s", channel_from, "\x1", params[0], slapcommands[mandate], "\x1");
	return;
}